use crate::client::error::GrpcClientError;
use std::time::Duration;
use tonic::Code;

/// 重试策略, 用于控制重试行为
#[derive(Debug, Clone)]
pub struct RetryPolicy {
    pub max_attempts: u32,                 // 最大重试次数
    pub initial_delay: Duration,           // 初始延迟
    pub max_delay: Duration,               // 最大延迟
    pub backoff_multiplier: f64,           // 退避因子
    pub retryable_status_codes: Vec<Code>, // 可重试的状态码
}

/// 重试执行器, 用于执行重试操作
/// 1.包括重试策略和重试操作, 并负责执行重试逻辑,
/// 2.包括计算延迟和判断是否重试, 以及执行重试操作
/// 3.包括处理重试错误和返回结果
impl RetryPolicy {
    pub fn new() -> Self {
        Self {
            max_attempts: 5,
            initial_delay: Duration::from_millis(100),
            max_delay: Duration::from_secs(30),
            backoff_multiplier: 1.5,
            retryable_status_codes: vec![Code::Unavailable, Code::Unknown, Code::DeadlineExceeded],
        }
    }

    /// 计算延迟时间, 根据重试次数和退避因子计算延迟时间
    pub fn calculate_delay(&self, attempt: u32) -> Duration {
        let delay_secs =
            self.initial_delay.as_secs_f64() * self.backoff_multiplier.powi(attempt as i32 - 1);
        let delay_secs = delay_secs.min(self.max_delay.as_secs_f64());
        Duration::from_secs_f64(delay_secs)
    }

    /// 判断是否应该重试, 根据重试次数和状态码判断是否应该重试
    pub fn should_retry(&self, status: &tonic::Status, attempt: u32) -> bool {
        attempt < self.max_attempts && self.retryable_status_codes.contains(&status.code())
    }

    /// 判断是否是网络错误, 根据错误类型判断是否是网络错误
    pub fn is_network_error(&self, error: &GrpcClientError) -> bool {
        match error {
            GrpcClientError::BrokenPipe(_) => true,
            GrpcClientError::Status(status) => self.retryable_status_codes.contains(&status.code()),
            _ => false,
        }
    }
}

/// 默认实现
impl Default for RetryPolicy {
    fn default() -> Self {
        Self::new()
    }
}
